/*
 * Copyright 2011 The Rusted Roof, LLC.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.proteusframework.platformservice.messaging;

import org.proteusframework.core.api.IProteusConstants;
import org.proteusframework.core.api.model.INamespace;
import org.proteusframework.platformservice.IServicePlugin;

import java.util.Properties;

/**
 * Provides an abstraction to an application messaging service provider, such as JMS or, in simpler
 * constructs, a Java BlockingQueue. The message service is dependent upon the physical existence of a primordial
 * {@link org.proteusframework.platformservice.persistence.api.IPersistenceService} at runtime, and it relies heavily
 * on the message bean construct.
 * <p/>
 * Every message service implementation can be assured that the following platform services have been brought online
 * and are immediately accessible the moment the {@link org.proteusframework.platformservice.IExtendedPlatformDelegate}
 * is injected:
 * <p/>
 * <ul>
 * <li>{@link org.proteusframework.platformservice.appconfig.IAppConfigService}</li>
 * <li>{@link org.proteusframework.platformservice.lifecycle.api.ILifecycleService}</li>
 * <li>{@link org.proteusframework.platformservice.persistence.api.IPersistenceService}</li>
 * </ul>
 *
 * @see org.proteusframework.platformservice.persistence.api.messagebean.IMessageBeanDescriptor
 * @see org.proteusframework.platformservice.persistence.api.IMessageBeanManager
 */
public interface IMessageService extends IServicePlugin
{
    /**
     * Hint provided via metadata properties when requesting a message sink that indicates a preference for a
     * maximum capacity. Typically this property is only evaluated when the message sink is first created.
     */
    public static final String KEY_MAXIMUM_CAPACITY = IProteusConstants.NAMESPACE + ".maxCapacity";

    /**
     * Hint provided via metadata properties when requesting a message sink that indicates a preference for a
     * minimum initial capacity. Typically this property is only evaluated when the message sink is first created.
     */
    public static final String KEY_MINIMUM_CAPACITY = IProteusConstants.NAMESPACE + ".minCapacity";

    /**
     * Hint provided via metadata properties when requesting a message sink that it should only support the
     * subscribers model and reject any attempts to register a sole listener.
     */
    public static final String KEY_SUBSCRIBER_MODEL = IProteusConstants.NAMESPACE + ".subscribersOnly";

    /**
     * Hint provided via metadata properties when requesting a message sink that it should only support a sole
     * registered listener model and reject any attempts to create subscribers.
     */
    public static final String KEY_LISTENER_MODEL = IProteusConstants.NAMESPACE + ".listenerOnly";

    /**
     * Creates an <code>IMessageSink</code> suitable for publishing message beans if a message sink doesn't already
     * exist with the given namespace, otherwise the existing message sink is returned.
     * <p/>
     * Concrete implementations must ensure that this method is implemented in a thread safe manner.
     *
     * @param namespace        Namespace used to uniquely identify the message sink. Both {@link IMessageSinkProducer} and
     *                         {@link IMessageSinkSubscriber} must access the same message sink.
     * @param messageBeanClass Message bean class
     * @return Message sink suitable for publishing message beans of the given class
     * @throws IllegalArgumentException when the message sink already exists, but there is a mis-match between the
     *                                  namespace and messageBeanClass.
     */
    <T extends IMessageBean> IMessageSink<T> requestMessageSink(INamespace namespace, Class<T> messageBeanClass)
            throws IllegalArgumentException;


    /**
     * Creates an <code>IMessageSink</code> suitable for publishing message beans if a message sink doesn't already
     * exist with the given namespace, otherwise the existing message sink is returned.
     * <p/>
     * Concrete implementations must ensure that this method is implemented in a thread safe manner.
     *
     *
     * @param namespace        Namespace used to uniquely identify the message sink. Both {@link org.proteusframework.platformservice.messaging.IMessageSinkProducer}
     *                         and {@link org.proteusframework.platformservice.messaging.IMessageSinkSubscriber} must access the same message sink.
     * @param messageBeanClass Message bean class
     * @param metadata         Additional metadata used to define the message sink. Concrete providers must provide
     *                         documentation on what types of metadata the require or support.
     * @return Message sink suitable for publishing message beans of the given class
     * @throws IllegalArgumentException when the message sink already exists, but there is a mis-match between the
     *                                  namespace and messageBeanClass.
     */
    <T extends IMessageBean> IMessageSink<T> requestMessageSink(INamespace namespace, Class<T> messageBeanClass, Properties metadata)
            throws IllegalArgumentException;

    /**
     * Determines if a message sink associated with the given namespace and message bean class already exists.
     * <p/>
     * <b>Note:</b> This method cannot be used in conjunction with either <code>requestMessageSink</code> variation
     * to safely determine if a message sink doesn't exist and that your request will create it. While one thread is
     * checking for existence, another thread could sneak in and create the message sink before your thread can.
     *
     * @param namespace        Namespace used to uniquely identify the message sink.
     * @param messageBeanClass Message bean class
     * @param <T>              Specific type of message bean
     * @return true, if a message sink has already been created
     */
    <T extends IMessageBean> boolean exists(INamespace namespace, Class<T> messageBeanClass);
}
